package battlecruiser.board;

import battlecruiser.board.cell.Cell;
import battlecruiser.board.cell.CellStatus;
import battlecruiser.ship.placement.fleet.Fleet;
import battlecruiser.shooter.ShotResult;
import battlecruiser.board.cell.coordinate.Coordinate;
import battlecruiser.ship.placement.ship.Ship;
import battlecruiser.shooter.DamageReport;
import org.apache.log4j.Logger;

import java.util.List;

/**
 * @author tymoshya
 * @since 14.06.12
 */
public class ObservableBoard extends Board {

    private static Logger LOG = Logger.getLogger(ObservableBoard.class);

    private Fleet fleet;
    private ObserverBoard observerBoard;

    // Mostly for debug and unit-tests
    public static ObservableBoard valueOf(String gameBoardAsString) {
        Cell[][] cells = initCells();
        List<String> statuses = getStatuses(gameBoardAsString);
        int statusIterator = 0;
        CellStatus cellStatus;
        for (int i = 0; i < getHeight(); i++) {
            for (int j = 0; j < getWidth(); j++) {
                cellStatus = CellStatus.fromString(statuses.get(statusIterator));
                statusIterator++;
                cells[i][j].setStatus(cellStatus);
            }
        }
        return new ObservableBoard(cells);
    }

    public ObservableBoard() {
        this.cells = initCells();
    }

    public ObservableBoard(Cell[][] cells) {
        this.cells = cells;
    }


    public void registerObserver(ObserverBoard observerBoard) {
        this.observerBoard = observerBoard;
        observerBoard.setObservableBoard(this);
    }

    public boolean isPlaceable(Ship ship) {
        // 1st and last is within the board
        if(!isWithinTheBoard(ship.getFirstDeckCoordinate()) || !isWithinTheBoard(ship.getLastDeckCoordinate())) {
            LOG.warn("Ship coordinates must be within the board! " + ship);
            return false;
        }

        // and all coordinates are free
        if(!getFreeCoordinates().containsAll(ship.getCoordinates())) {
            LOG.warn("Can place ship deck only on the FREE cell! " + ship + " Check board: \n" + this);
            return false;
        }

        return true;
    }

    public void drawShip(Ship ship) {
        List<Coordinate> shipDecks = ship.getCoordinates();
        for (Coordinate coordinate : shipDecks) {
            cells[coordinate.getI()][coordinate.getJ()].setStatus(CellStatus.SHIP);
        }
        drawShipBorderWaters(ship);
    }

    public DamageReport processIncomingShot(Coordinate target) {
        DamageReport result;
        if (target != null) {
            result = fleet.reportDamage(target);
            ShotResult shotResult = result.getShotResult();
            if (shotResult == ShotResult.MISSED) {
                drawMissed(target);
                observerBoard.drawMissed(target);
            } else {
                Ship ship = result.getShipUnderAttack();
                drawHit(target);
                observerBoard.drawHit(target);
                if (isDestroyed(ship)) {
                    drawDestroyed(ship);
                    observerBoard.drawDestroyed(ship);
                    fleet.destroyShip(ship);
                }
            }
        } else {
            result = null;
        }
        return result;
    }

    public Fleet getFleet() {
        return fleet;
    }

    public void setFleet(Fleet fleet) {
        this.fleet = fleet;
    }
}
